using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security;
using WindowsAPI.Service;

namespace WindowsAPI
{
    [SuppressUnmanagedCodeSecurity, Dll(FileName)]
    public static class Ws2_32
    {
        /* ------------------------------------------------------------------------------------------ */

        public const string FileName = "Ws2_32.dll";

        public const int SOCKET_ERROR = -1;
        public const int INVALID_SOCKET = ~0;

        /* ------------------------------------------------------------------------------------------ */

        [DllImport(FileName), ManagedAlternative("System.Net.Sockets.Socket.Accept()")]
        public static extern SOCKET accept(SOCKET s, IntPtr addr, int addrsize);

        [DllImport(FileName)]
        public static extern int WSAStartup(ushort Version, out WSAData Data);

        [DllImport(FileName)]
        public static extern SocketError WSAGetLastError();

        [DllImport(FileName)]
        public static extern SOCKET socket(AddressFamily af, SocketType type, ProtocolType protocol);

        [DllImport(FileName)]
        public static extern int send(SOCKET s, [MarshalAs(UnmanagedType.LPArray)] byte[] buf, int len, int flags);

        [DllImport(FileName)]
        public static extern int send(SOCKET s, IntPtr buf, int len, int flags);

        [DllImport(FileName)]
        public static extern int recv(SOCKET s, [MarshalAs(UnmanagedType.LPArray), In, Out] ref byte[] buf, int len, int flags);

        [DllImport(FileName)]
        public static extern int recv(SOCKET s, IntPtr buf, int len, int flags);

        [DllImport(FileName, CharSet = CharSet.Ansi)]
        public static extern uint inet_addr(string cp);

        [DllImport(FileName)]
        public static extern int listen(SOCKET s, int backlog);

        [DllImport(FileName)]
        public static extern ushort htons(ushort hostshort);

        [DllImport(FileName)]
        public static extern int connect(SOCKET s, ref sockaddr_in addr, int addrsize);

        [DllImport(FileName)]
        public static extern int closesocket(SOCKET s);

        [DllImport(FileName)]
        public static extern int getpeername(SOCKET s, ref sockaddr_in addr, ref int addrsize);

        [DllImport(FileName)]
        public static extern int bind(SOCKET s, ref sockaddr_in addr, int addrsize);

        [DllImport(FileName)]
        public static extern int select(
                    int nfds,
            /* An optional pointer to a set of sockets to be checked for readability. */
                    ref fd_set readfds,
            /* An optional pointer to a set of sockets to be checked for writability. */
                    ref fd_set writefds,
            /* An optional pointer to a set of sockets to be checked for errors. */
                    ref fd_set exceptfds,
                    ref timeval timeout);

        [DllImport(FileName), Obsolete]
        public static extern string inet_ntoa(sockaddr_in.in_addr @in);

        /* ------------------------------------------------------------------------------------------ */
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct timeval
    {
        public long tv_sec;
        public long tv_usec;
    }

    public enum AddressFamily
    {
        Unknown = 0,
        InterNetworkv4 = 2,
        Ipx = 4,
        AppleTalk = 17,
        NetBios = 17,
        InterNetworkv6 = 23,
        Irda = 26,
        BlueTooth = 32
    }

    public enum SocketType
    {
        Unknown = 0,
        Stream = 1,
        DGram = 2,
        Raw = 3,
        Rdm = 4,
        SeqPacket = 5
    }

    public enum ProtocolType
    {
        BlueTooth = 3,
        Tcp = 6,
        Udp = 17,
        ReliableMulticast = 113
    }

    public unsafe struct SOCKET
    {
        private void* handle;
        private SOCKET(int _handle) { handle = (void*)_handle; }

        public static bool operator ==(SOCKET s, int i) { return ((int)s.handle == i); }
        public static bool operator !=(SOCKET s, int i) { return ((int)s.handle != i); }
        public static implicit operator SOCKET(int i) { return new SOCKET(i); }
        public static implicit operator uint(SOCKET s) { return (uint)s.handle; }
        public override bool Equals(object obj)
        {
            return (obj is SOCKET) ? (((SOCKET)obj).handle == handle) : base.Equals(obj);
        }
        public override int GetHashCode() { return (int)handle; }
    }

    public struct fd_set
    {
        public const int FD_SETSIZE = 64;
        public uint fd_count;

        [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4, SizeConst = FD_SETSIZE)]
        public uint[] fd_array;//[FD_SETSIZE];
    }

    // C# equivilent to C++'s sockaddr_in / SOCKADDR_IN
    [StructLayout(LayoutKind.Sequential, Size = Size)]
    public struct sockaddr_in
    {
        public const int Size = 16;

        public short sin_family;
        public ushort sin_port;
        public struct in_addr
        {
            public uint S_addr;
            public struct _S_un_b { public byte s_b1, s_b2, s_b3, s_b4; }
            public _S_un_b S_un_b;
            public struct _S_un_w { public ushort s_w1, s_w2; }
            public _S_un_w S_un_w;
        }
        public in_addr sin_addr;
    }

    // WSAData structure, used in WSAStarutp
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public unsafe struct WSAData
    {
        public ushort Version;
        public ushort HighVersion;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
        public string Description;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)]
        public string SystemStatus;
        public ushort MaxSockets;
        public ushort MaxUdpDg;
        sbyte* lpVendorInfo;
    }

}